/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.spring.beanconext.provider;


import org.springframework.context.ConfigurableApplicationContext;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContextDelegate;
import com.massyframework.beanskin.assembly.handling.ActivationHandler;
import com.massyframework.beanskin.assembly.handling.HandlerRegistration;
import com.massyframework.beanskin.spring.beancontext.DependencyServiceBeanDefinitionRegistryPostProcessor;
import com.massyframework.beanskin.spring.beancontext.DependencyServiceInitializer;
import com.massyframework.beanskin.spring.beancontext.SpringBeanContextAdapter;
import com.massyframework.beanskin.spring.beancontext.StandloneApplicationContextFactory;
import com.massyframework.beanskin.util.classloading.ClassLoaderUtils;

/**
 * {@link StandloneHandler} 提供基于独立Spring容器的抽象语义。
 */
public final class StandloneHandler extends DependencyServiceBeanDefinitionRegistryPostProcessor
        implements ActivationHandler {

    private volatile HandlerRegistration<AssemblyContextDelegate<SpringBeanContextAdapter>> registration;

    /**
     * 构造方法
     */
    public StandloneHandler() {
    }

    /**
     * 启动,使得进入激活状态
     *
     * @throws Exception 启动时发生非预期例外
     */
    @Override
    public synchronized void doStarting() throws Exception {
        if (this.registration == null){
        	Assembly assembly = this.getAssociatedAssembly();
            SpringBeanContextAdapter adapter =
                    this.createBeanContext(assembly);
            AssemblyContextDelegate<SpringBeanContextAdapter> delegate =
            		new AssemblyContextDelegate<>(adapter);
            delegate.init(this.getLifecycleManager());
            this.setAssemblyContext(delegate);

            ClassLoader contextLoader = ClassLoaderUtils.setThreadContextClassLoader(
                    assembly.getModuleClassLoader());
            try{
                adapter.getApplicationContext().refresh();

                this.registration =
                        this.getLifecycleManager()
                                .register(delegate);
            }finally{
                ClassLoaderUtils.setThreadContextClassLoader(contextLoader);
            }
        }
    }

    /**
     * 已停止，退出激活状态
     *
     * @throws Exception 停止时发生非预期例外
     */
    @Override
    public synchronized void doStopped() throws Exception {
        if (this.registration != null){
            AssemblyContextDelegate<SpringBeanContextAdapter> context =
                    this.registration.getHandler();
            this.registration.unregister();
            this.registration = null;

            ClassLoader contextLoader =
                    ClassLoaderUtils.setThreadContextClassLoader(this.getAssociatedAssembly().getModuleClassLoader());
            try{
                context.getBeanContext().getApplicationContext().close();
            }finally{
            	this.setAssemblyContext(null);
                ClassLoaderUtils.setThreadContextClassLoader(contextLoader);
            }
        }
    }

    /**
     * 基于{@link ServiceExportableModule} 创建Spring容器，并返回封装的适配器
     * @param assembly {@link Assembly}, 装配件
     * @return {@link SpringBeanContextAdapter}
     */
    protected SpringBeanContextAdapter createBeanContext(Assembly assembly){
        StandloneApplicationContextFactory factory
                = new StandloneApplicationContextFactory();

        DependencyServiceInitializer<ConfigurableApplicationContext> initializer =
        		new DependencyServiceInitializer<ConfigurableApplicationContext>(this);
        ConfigurableApplicationContext applicationContext =
        		factory.createApplicationContext(assembly, initializer);
        SpringBeanContextAdapter result =new SpringBeanContextAdapter(applicationContext);
        return result;
    }
}
